import React, { ReactElement } from 'react';
import { NativeModules } from 'react-native';
import { getFreeDiskStorageSync } from 'react-native-device-info';
import { request, PERMISSIONS, RESULTS } from 'react-native-permissions';
import RNFS from 'react-native-fs';
import { unzip } from 'react-native-zip-archive';

import { Dispatch, State } from './types';
import { Data } from '../../api/class/getLessons';
import {
  LessonShowInfo,
  UserShowInfo
} from '../../api/course/getUserCoursePage';
import { ClassApi } from '../../api';
import navigator from '../../util/navigator';
import {
  CurrentRoundInfo,
  Data as RoomInfoData
} from '../../api/class/getRoomInfo';
import { loading, modalClose, modalOpen, toolTipOpen } from '../../util/global';
import { ClassRoomType, LessonStatus, UserRole } from '../ClassRoom/constant';
import { getPermission, openPermissionModal } from '../../util/permission';
import { DEBUG, DEVICE_BRAND_VIVO, isIOS, Permission } from '../../constant';
import { shellExec, systemUtil } from '../../util/native';
import YutouAlert from '../../components/YutouAlert';
import { LESSON_RESOURCE_STATUS } from './constant';
import { InitVideoTaskProps } from '../../util/videoDownload/types';
import { getVideoName } from '../ClassRoom/actions';
import { VIDEO_TYPE } from '../../util/videoDownload/constant';
import {
  filterExcludeVideoTasks,
  startDownloadByParentId,
  startInitDownloadVideoTasks
} from '../../util/videoDownload/videoDownloadManage';
import Log from '../../common/log';
import httpServer from '../../util/native/httpServer';
import { DOWNLOAD_HTML_PATH } from '../Login/constant';
import PortraitAlert from '../../components/Alert/PortraitAlert';
import customAwait from '../../util/customAwait';
import rnfsDownload from '../../util/videoDownload/rnfsDownload';
import { getUserInfo } from '../../store/user';
import { getOsVersionIsLow } from '../../util/osVersion';
import { getProblemWebview, setProblemWebview } from '../../store/appConfig';

let dispatch: Dispatch;
let state: State;
let reload = (): void => {};

const TAG = 'CourseAction';

export default (d: Dispatch, s: State): void => {
  dispatch = d;
  state = s;
};

export const getReload = (): void => {
  reload();
};

export const setLessons = (value: Data): void => {
  dispatch({ type: 'SET_LESSON', value });
};

export const fetchFailed = (): void => {
  dispatch({ type: 'FETCH_FAILED' });
};

// 获取数据
export const getLessons = (courseId: number): void => {
  reload = (): void => {
    getLessons(courseId);
  };
  ClassApi.getLessons({ courseId }).then((res): void => {
    if (res.code === 0) {
      setLessons(res.data);
    }
  });
};

export const downloadProblemWebView = async (): Promise<void> => {
  let isDownloadProblemHtmlSuccess = false;

  loading.show();
  const { problemWebviewName } = getProblemWebview();

  const DOWNLOAD_PROBLEM_HTML_ZIP_PATH = `${DOWNLOAD_HTML_PATH}/problem.zip`;
  const DOWNLOAD_PROBLEM_HTML_PATH = `${DOWNLOAD_HTML_PATH}/problem`;
  const isZipExist = await RNFS.exists(DOWNLOAD_PROBLEM_HTML_ZIP_PATH);
  const isHtmlExist = await RNFS.exists(DOWNLOAD_PROBLEM_HTML_PATH);
  if (isZipExist) {
    await RNFS.unlink(DOWNLOAD_PROBLEM_HTML_ZIP_PATH);
  }
  if (isHtmlExist) {
    await RNFS.unlink(DOWNLOAD_PROBLEM_HTML_PATH);
  }
  const options = {
    fromUrl: DEBUG
      ? `http://resource.mifengxiaoban.com/webview/d/${problemWebviewName}.zip`
      : `http://resource.mifengxiaoban.com/webview/p/${problemWebviewName}.zip`,
    toFile: DOWNLOAD_PROBLEM_HTML_ZIP_PATH,
    background: true,
    connectionTimeout: 8000,
    readTimeout: 8000
  };
  const ret = await rnfsDownload(options);
  const [error, res] = await customAwait(ret.promise);
  if (error) {
    Log.i(
      TAG,
      JSON.stringify({
        event: 'problemHtmlDownloadFail',
        error
      })
    );
    return;
  }
  if (res) {
    const { statusCode } = res;
    if (statusCode === 200) {
      Log.i(
        TAG,
        JSON.stringify({
          event: 'problemHtmlDownloadSuccessInCourse',
          res
        })
      );
      // 解压
      const [unzipError, unzipRes] = await customAwait(
        unzip(DOWNLOAD_PROBLEM_HTML_ZIP_PATH, DOWNLOAD_PROBLEM_HTML_PATH)
      );
      if (unzipError) {
        Log.i(
          TAG,
          JSON.stringify({
            event: 'unzipProblemHtmlDownloadFailInCourse',
            error: unzipError
          })
        );
      }
      if (unzipRes) {
        isDownloadProblemHtmlSuccess = true;
        setProblemWebview({ needDownload: false });
        Log.i(
          TAG,
          JSON.stringify({
            event: 'unzipProblemHtmlDownloadSuccessInCourse',
            res: unzipRes
          })
        );
      }
    } else {
      Log.i(
        TAG,
        JSON.stringify({
          event: 'problemHtmlDownloadFailInCourse',
          error,
          statusCode
        })
      );
    }
  }
  loading.hide();

  if (isDownloadProblemHtmlSuccess) {
    toolTipOpen({
      desc: '课程相关资源加载成功，请重新进入教室'
    });
  } else {
    modalOpen({
      Type: (): React.ReactElement => (
        <PortraitAlert
          title="课程资源加载失败"
          subTitle="课程相关资源加载失败，无法进入教室，请重新下载"
          sureDesc="重新下载"
          sureAction={(): void => {
            downloadProblemWebView();
            modalClose();
          }}
          cancelDesc="返回"
          cancelAction={(): void => {
            modalClose();
          }}
        />
      ),
      needBack: true
    });
  }
};

// 检查problemWebView加载状态
export const checkProblemWebView = async (): Promise<boolean> =>
  new Promise(
    async (resolve, reject): Promise<void> => {
      const { needDownload } = getProblemWebview();

      if (!needDownload) {
        resolve(true);
        return;
      }

      modalOpen({
        Type: (): React.ReactElement => (
          <PortraitAlert
            title="课程资源加载失败"
            subTitle="课程相关资源加载失败，无法进入教室，请重新下载"
            sureDesc="重新下载"
            sureAction={(): void => {
              downloadProblemWebView();
              modalClose();
            }}
            cancelDesc="返回"
            cancelAction={(): void => {
              modalClose();
            }}
          />
        ),
        needBack: true
      });
      resolve(false);
    }
  );

const getIosCanPushClassRoom = (): Promise<boolean> =>
  new Promise(
    async (resolve, reject): Promise<void> => {
      const result = await request(PERMISSIONS.IOS.CAMERA);
      if (result !== RESULTS.GRANTED) {
        openPermissionModal('相机', (): void => {}, true);
        resolve(false);
        return;
      }

      const checkProblemWebViewRes = await checkProblemWebView();
      resolve(checkProblemWebViewRes);
    }
  );

const checkVivoPermission = (): Promise<boolean> =>
  new Promise(
    async (resolve, reject): Promise<void> => {
      const [error, r] = await customAwait(
        systemUtil.checkPermission('android.permission.CAMERA')
      );
      if (r && r['android.permission.CAMERA'] === Permission.Granted) {
        const checkProblemWebViewRes = await checkProblemWebView();
        resolve(checkProblemWebViewRes);
        return;
      }

      openPermissionModal('相机', (): void => {}, true);
      resolve(false);
    }
  );

const checkCommonPermission = (): Promise<boolean> =>
  new Promise(
    async (resolve, reject): Promise<void> => {
      const [permissionsDesc, r] = await customAwait(getPermission());
      if (permissionsDesc) {
        openPermissionModal(permissionsDesc, (): void => {}, true);
        resolve(false);
        return;
      }

      const checkProblemWebViewRes = await checkProblemWebView();
      resolve(checkProblemWebViewRes);
    }
  );

const pushClassRoom = (
  lessonId: number,
  lessonResourceId: number,
  lessonSource: string,
  roomInfo: RoomInfoData,
  assistant: UserShowInfo
): void => {
  httpServer.start();
  const {
    channel,
    currentRoundInfo = {},
    startImageURL,
    startVideoURL,
    intervalBeforeLessonStart,
    lessonName,
    isSign
  } = roomInfo;
  const { roundId, roundStatus } = (currentRoundInfo as CurrentRoundInfo) || {};
  const { role } = getUserInfo();
  const isParent = role === UserRole.Parent;
  navigator.push('ClassRoom', {
    lessonId,
    channel,
    startRoundId: roundId,
    lessonResourceId,
    welcomeData: {
      startImageURL,
      startVideoURL
    },
    intervalBeforeLessonStart,
    lessonName,
    lessonSource,
    roundStatus,
    needSign: !isSign && !isParent,
    assistantInfo: assistant
  });
};

// 获取房间信息
export const getRoomInfo = async (
  lessonId: number,
  lessonResourceId: number,
  lessonSource: string,
  assistant: UserShowInfo
): Promise<void> => {
  shellExec.makeSureLongLinkConnected();
  const res = await ClassApi.getRoomInfo({ lessonId }, true);
  if (res.code !== 0) return;

  const { lessonStatus } = res.data;
  if (lessonStatus === LessonStatus.UnStart) {
    toolTipOpen({ desc: '课程未开始' });
    return;
  }

  if (lessonStatus === LessonStatus.End) {
    toolTipOpen({ desc: '课程已结束' });
    return;
  }

  loading.show();

  if (isIOS) {
    const iosCanPushClassRoom = await getIosCanPushClassRoom();
    loading.hide();
    if (!iosCanPushClassRoom) return;

    pushClassRoom(
      lessonId,
      lessonResourceId,
      lessonSource,
      res.data,
      assistant
    );
    return;
  }

  const checkCommon = async (): Promise<void> => {
    const canPushClassRoom = await checkCommonPermission();
    loading.hide();
    if (!canPushClassRoom) return;
    pushClassRoom(
      lessonId,
      lessonResourceId,
      lessonSource,
      res.data,
      assistant
    );
  };

  try {
    if (
      systemUtil.getDeviceProduct() === DEVICE_BRAND_VIVO &&
      NativeModules.SystemUtil.checkPermission
    ) {
      const osVersionIsLow = getOsVersionIsLow();
      if (!osVersionIsLow) {
        const vivoCanPushClassRoom = await checkVivoPermission();
        loading.hide();
        if (!vivoCanPushClassRoom) return;
      }

      loading.hide();

      pushClassRoom(
        lessonId,
        lessonResourceId,
        lessonSource,
        res.data,
        assistant
      );
      return;
    }

    checkCommon();
  } catch (error) {
    Log.e(TAG, JSON.stringify({ event: 'getRoomInfo', error }));
    checkCommon();
  }
};

// 回放课程
export const vodLesson = async (
  lessonId: number,
  lessonResourceId: number,
  lessonName: string,
  lessonSource: string,
  assistant: UserShowInfo,
  startTime: string
): Promise<void> => {
  const { role } = getUserInfo();
  const isParent = role === UserRole.Parent;
  if (isParent) {
    toolTipOpen({ desc: '家长账号不能看回放' });
    return;
  }
  const hasProblemWebView = await checkProblemWebView();
  if (!hasProblemWebView) {
    return;
  }
  httpServer.start();
  navigator.push('ClassRoom', {
    lessonId,
    channel: String(new Date().getTime()),
    lessonResourceId,
    type: ClassRoomType.Vod,
    lessonName,
    lessonSource,
    assistantInfo: assistant,
    startTime
  });
};

export const alertDownload = async (
  lessonResourceId: number,
  lessonName: string,
  lessonSource: string,
  startTime?: string
): Promise<void> => {
  const cacheInfoRes = await ClassApi.getRoundListCacheInfo({
    lessonResourceId
  });

  if (cacheInfoRes.code === 0) {
    const { videos, resourceStatus } = cacheInfoRes.data;

    if (resourceStatus === LESSON_RESOURCE_STATUS.Offline) {
      toolTipOpen({ desc: '课程资源暂未上线' });
      return;
    }
    const downloadVideoTasks: InitVideoTaskProps[] = [];

    const getDownloadVideoTasks = (
      urls: string[],
      size: number
    ): InitVideoTaskProps => {
      // http://video.yutouedu.com/ak/cp/p-r-cf62659025b523c5bd3d231c8777d786-c/hd.m3u8 -> cf62659025b523c5bd3d231c8777d786
      const title = getVideoName(urls[0]);
      return {
        parentId: lessonResourceId,
        parentTitle: lessonName,
        id: `${lessonResourceId}-${title}`,
        title,
        type: VIDEO_TYPE.hd,
        urls,
        size,
        source: lessonSource
      };
    };

    let needSize = 0;

    videos.forEach((item): void => {
      const { originVideoDownloadURL, originVideoSize } = item;
      downloadVideoTasks.push(
        getDownloadVideoTasks([originVideoDownloadURL], originVideoSize)
      );

      needSize += originVideoSize;
    });

    const freeSpace = isIOS
      ? getFreeDiskStorageSync() // Bytes
      : Number(systemUtil.getMemoryInfo().availMemory) * 1024 * 1024; // MB -> KB -> Bytes

    if (freeSpace < needSize) {
      modalOpen({
        Type: (): ReactElement => (
          <YutouAlert
            title="温馨提示"
            desc="手机存储空间不足，无法加载课件资源包。为保障流畅体验，请先清理缓存再开始上课。"
            sureDesc="缓存管理"
            sureAction={(): void => {
              navigator.push('DownloadManage');
              modalClose();
            }}
          />
        ),
        needBack: true
      });
      return;
    }

    modalOpen({
      Type: (): ReactElement => (
        <YutouAlert
          title="温馨提示"
          desc={`教室${
            startTime ? `将于${startTime}开放` : '还未开放'
          }，为了保障上课流畅，现在可以提前下载课程资源包。`}
          sureDesc={`下载(约${(needSize / 1024 / 1024).toFixed()}M)`}
          sureAction={(): void => {
            filterExcludeVideoTasks(downloadVideoTasks);
            startInitDownloadVideoTasks(downloadVideoTasks);
            startDownloadByParentId(lessonResourceId);
            toolTipOpen({ desc: '课程资源已添加到缓存列表中' });
            modalClose();
          }}
        />
      ),
      needBack: true
    });
  }
};

export const onPressLesson = ({
  status,
  lessonResourceId,
  name,
  id,
  source,
  startTime,
  assistant
}: LessonShowInfo): void => {
  if (status === LessonStatus.UnStart) {
    if (startTime && startTime !== '') {
      const time = new Date(startTime.replace(/-/g, '/'));

      const openTime = new Date(time.getTime() - 10 * 60 * 1000);
      const hour = openTime.getHours();
      const minute = openTime.getMinutes();
      alertDownload(
        lessonResourceId,
        name,
        source,
        `${hour}:${minute < 10 ? `0${minute}` : minute}`
      );
      return;
    }
    alertDownload(lessonResourceId, name, source);
    return;
  }
  if (status === LessonStatus.HaveStart || status === LessonStatus.EarlyEntry) {
    getRoomInfo(id, lessonResourceId, source, assistant);
    return;
  }
  if (status === LessonStatus.End) {
    vodLesson(id, lessonResourceId, name, source, assistant, startTime);
  }
};
